﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.Linq;
using System.Windows.Forms;
using System.IO;
using System.Media;
using System.Threading;
using System.Xml;
using System.Runtime.Serialization;
using System.Runtime;
using System.Globalization;

namespace Battle
{
    public class Tablee : Control
    {
        private const int CellSize = 50;

        private readonly TableCell[,] _cells;
        private readonly List<Ship> _ships;
        private ShipDrag _shpDragged;
        private readonly Random _random;
        private readonly bool _shipsDraw;
        internal int modeButtonSettings = 0;
        private readonly string _header = "ABSDEFGHIJ";
        private static readonly Rectangle TableArea = new Rectangle(0, 0, 10, 10);

        public new event EventHandler<ShootingEventArgs> OnClick;
        public TableStageEnum Mode { get; set; }        

        public Tablee():this(true)
        {

        }

        public Tablee(bool drawShips)
        {
            _shipsDraw = drawShips;
            _cells = new TableCell[10,10];
            _ships = new List<Ship>();
            _random = new Random();
            Mode = TableStageEnum.Preparation;
            Margin = Padding.Empty;

            CreateTable();
        }

        public Tablee(SettingsForm set, bool drawShips ): this(true)
        {
            _shipsDraw = drawShips;
            modeButtonSettings = set.radioButtonSoviet.Checked ? 0 : 1;
        }

// Задання характеристик для лейб
        private static Label CreateCaptionCell(int x, int y, string text)
        {
            var cell = new Label
                           {
                               AutoSize = false,
                               BackColor = Color.FromArgb(45,177,252),
                               TextAlign = ContentAlignment.MiddleCenter,
                               Text = text,
                               Location = new Point(x, y),
                               Width = CellSize,
                               Height = CellSize,
                               Font = new System.Drawing.Font("Microsoft Sans Serif", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(204)))
                           };
            return cell;
        }

// Відображеня заголовків поля      
        private void CreateCaptions()
        {
            var l = new Label
                         {
                             Location = new Point(0, 0),
                             Width = CellSize,
                             Height = CellSize,
                            //Image = Image.FromFile(Application.StartupPath + "\\" + @"images.jpg")
                            Image=Resource1.images
                         };
            Controls.Add(l);

            for (var i = 0; i < TableArea.Width; i++)
            {
                var offset = CellSize * i + CellSize;
                var columnHeader = CreateCaptionCell(offset, 0, (_header[i]).ToString());
                var rowHeader = CreateCaptionCell(0, offset, (i + 1).ToString());
                Controls.Add(columnHeader);
                Controls.Add(rowHeader);
            }
        }

// Створення поля
        private void CreateTable()
        {
            var boardSize = new Size(CellSize * TableArea.Width + CellSize, CellSize * TableArea.Height + CellSize);
            base.MinimumSize = boardSize;
            base.MaximumSize = boardSize;
            CreateCaptions();
            var points = TableArea.PointsOf();
            foreach(var point in points)
            {
                var cell = new TableCell(point.X, point.Y)
                               {
                                   Top = point.X * CellSize + CellSize,
                                   Left = point.Y * CellSize + CellSize,
                                   Width = CellSize,
                                   Height = CellSize,
                                   State = TableCellCollocateEnum.Average
                               };
                _cells[point.X, point.Y] = cell;
                cell.MouseDown += OnCellMouseDown;
                cell.DragEnter += OnCellDragEnter;
                cell.DragLeave += OnCellDragLeave;
                cell.DragDrop += OnCellDragDrop;
                cell.QueryContinueDrag += OnCellQueryContinueDrag;
                cell.Click += OnCellClick;
                Controls.Add(cell);
            }
        }

        private void OnCellClick(object sender, EventArgs e)
        {
            if (Mode != TableStageEnum.Fight)
            {
                return;
            }
            var handler = OnClick;
            if (handler == null)
            {
                return;
            }
            var cell = (TableCell)sender;
            var eventArgs = new ShootingEventArgs(cell.X, cell.Y);
            handler(this, eventArgs);
        }

// Отримати корабель за координатами      
        private Ship GetShipAt(int x, int y)
        {
            return _ships.FirstOrDefault(ship => ship.LocatedIn(x, y));
        }

 // Реалізація Drag-and-Drop
        private void OnCellMouseDown(object sender, MouseEventArgs e)
        {
            if (Mode == TableStageEnum.Fight || !_shipsDraw)
            {
                return;
            }
            var cell = (TableCell)sender;
            var ship = GetShipAt(cell.X, cell.Y);
            if (ship == null)
            {
                return;
            }
            _shpDragged = ShipDrag.Out(ship);
            cell.DoDragDrop(ship, DragDropEffects.Copy | DragDropEffects.Move);
        }
 
        private void OnCellQueryContinueDrag(object sender, QueryContinueDragEventArgs e)
        {
            var shouldRotate = ((e.KeyState & 32) == 32);// Натиснуто ALT 
            var isRotated = _shpDragged.IsOrientationMutate;

            if ((shouldRotate && isRotated) || (!shouldRotate && !isRotated))
                return;
            var rect = _shpDragged.GetShipArea();
            RePaintRefion(rect);
            _shpDragged.Turn();
            _shpDragged.IsOrientationMutate = !isRotated;
            var state = DropShip(_shpDragged, _shpDragged.X, _shpDragged.Y) ? TableCellCollocateEnum.Dragged : TableCellCollocateEnum.DragInvalid;
            DrawShip(_shpDragged, state);
        }

        private void OnCellDragEnter(object sender, DragEventArgs e)
        {
            if (e.Data.GetDataPresent(typeof(Ship)))
            {                          
                var cell = (TableCell)sender;
                _shpDragged.MoveTo(cell.X, cell.Y);
                var canPlaceShip = DropShip(_shpDragged, cell.X, cell.Y);
                var state = canPlaceShip ? TableCellCollocateEnum.Dragged : TableCellCollocateEnum.DragInvalid;
                DrawShip(_shpDragged, state);
                e.Effect = canPlaceShip ? DragDropEffects.Move : DragDropEffects.None;
            }
            else
            {
                e.Effect = DragDropEffects.None;
            }
        }

        private void OnCellDragLeave(object sender, EventArgs e)
        {
            var rect = _shpDragged.GetShipArea();
            RePaintRefion(rect);
        }

        private void OnCellDragDrop(object sender, DragEventArgs e)
        {
            var cell = (TableCell)sender;
            if (e.Data.GetDataPresent(typeof(Ship)))
            {
                if (!DropShip(_shpDragged, cell.X, cell.Y))
                {
                    return;
                }
                var ship = _shpDragged.AreaOfShip;
                _ships.Remove(ship);
                var rect = ship.GetShipArea();
                RePaintRefion(rect);
                ship.Orient = _shpDragged.Orient;
                ShipPlus(ship, cell.X, cell.Y);
                _shpDragged = null;
            }
            else
            {
                e.Effect = DragDropEffects.None;
            }
        }

// Ддовання корабля на поле
        public void ShipPlus(Ship ship, int x, int y)
        {
            ship.MoveTo(x, y);
            _ships.Add(ship);
            DrawShip(ship, TableCellCollocateEnum.Ship);
        }

// Перевірка на можливість поставлення корабля при рандомному розставленні чи при Drag-and-Drop  
        private bool DropShip(Ship ship, int x, int y)
        {
            var shipRegion = ship.GetShipArea();
            shipRegion.X = x;
            shipRegion.Y = y;
            if (!TableArea.Include(shipRegion))
            {
                return false;
            }
            shipRegion.X -= 1;
            shipRegion.Y -= 1;
            shipRegion.Width += 2;
            shipRegion.Height += 2;
            foreach (var s in _ships)
            {
                if (ship is ShipDrag && s == ((ShipDrag)ship).AreaOfShip)
                {
                    continue;
                }

                if (s.GetShipArea().CrossWith(shipRegion))
                {
                    return false;
                }
            }
            return true;
        }

// Перемальовка області корабля
        private void RePaintRefion(Rectangle region)
        {
            var points = region.PointsOf();
            foreach (var point in points)
            {
                if (!TableArea.Include(point))
                {
                    continue;
                }
                var ship = GetShipAt(point.X, point.Y);
                _cells[point.X, point.Y].State = ship == null ? TableCellCollocateEnum.Average : TableCellCollocateEnum.Ship;
            }
        }

// Відображення прихованних кораблів компютера при закінченні гри
        public void ShowShips()
        {
            foreach(var ship in _ships)
            {
                var shipPoints = ship.GetShipArea().PointsOf();

                foreach(var point in shipPoints)
                {
                    var cell = _cells[point.X, point.Y];
                    if (cell.State != TableCellCollocateEnum.Average)
                    {
                        continue;
                    }
                    cell.State = TableCellCollocateEnum.Ship;                        
                }
            }
        }

// Малювання корабля при Drag-and-Drop  
        private void DrawShip(Ship ship, TableCellCollocateEnum state)
        {
            DrawShip(ship, state, false);
        }

// Малювання навколо потонулого корабля клітик, в які стріляти не модна
        private void DrawAround(Ship ship, TableCellCollocateEnum state)
        {
            var points = ship.GetShipArea().PointsOf();
            foreach (var point in points)
            {
                if (TableArea.Include(point))
                {
                    if (point.X + 1 < _cells.GetLength(0))
                    {
                        _cells[point.X + 1, point.Y].State = state;
                    }
                    if (point.X + 1 < _cells.GetLength(0) && (point.Y + 1 < _cells.GetLength(0)))
                    {
                        _cells[point.X + 1, point.Y + 1].State = state; 
                    }
                    if ((point.X + 1 < _cells.GetLength(0)) && (point.Y - 1 >= 0))
                    {
                        _cells[point.X + 1, point.Y - 1].State = state;
                    }
                    if ((point.Y + 1 < _cells.GetLength(0)) && (point.X - 1 >= 0))
                    {
                        _cells[point.X - 1, point.Y + 1].State = state;
                    }
                    if ((point.Y - 1 >= 0) && (point.X - 1 >= 0))
                    {
                        _cells[point.X - 1, point.Y - 1].State = state;
                    }
                    if ((point.X - 1 >= 0))
                    {
                        _cells[point.X - 1, point.Y].State = state;
                    }
                    if (point.Y + 1 < _cells.GetLength(0))
                    {
                        _cells[point.X, point.Y + 1].State = state;
                    }
                    if ((point.Y - 1 >= 0))
                    {
                        _cells[point.X, point.Y - 1].State = state;
                    }                    
                }
            }

        }

// Малювання корабля при перепострілі
        private void DrawShip(Ship ship, TableCellCollocateEnum state, bool ok)
        {
            if (!_shipsDraw && !ok)
            {
                return;
            }
            var points = ship.GetShipArea().PointsOf();
            foreach (var point in points)
            {
                if (TableArea.Include(point))
                {
                    _cells[point.X, point.Y].State = state;
                }
            }
        }

// Очищення поля при додаванні рандомних кораблів компютера
        public void ClearTable()
        {
            _ships.Clear();
            var points = TableArea.PointsOf();
            foreach (var point in points)
            {
                _cells[point.X, point.Y].State = TableCellCollocateEnum.Average;
            }
        }

// Додавання рандомних кораблів
        public void AddRndShips()
        {
            ClearTable();
            var ships = GenerateShips();
            foreach (var ship in ships)
            {
                var shipAdded = false;
                while (!shipAdded)
                {
                    var x = _random.Next(10);
                    var y = _random.Next(10);

                    if (!DropShip(ship, x, y))
                    {
                        continue;
                    }                    
                    ShipPlus(ship, x, y);
                    shipAdded = true;
                }
            }
        }
       
// Заповнення ліста кораблів з рандомним напрямком для американського або рядянського режимів
        private IList<Ship> GenerateShips()
        {
            if (modeButtonSettings == 1)
            {
                var ships = new List<Ship>
                        {
                            new Ship(4){Orient = (ShipOrientationEnum)_random.Next(2)},
                            new Ship(3){Orient = (ShipOrientationEnum)_random.Next(2)},
                            new Ship(3){Orient = (ShipOrientationEnum)_random.Next(2)},
                            new Ship(2){Orient = (ShipOrientationEnum)_random.Next(2)},
                            new Ship(2){Orient = (ShipOrientationEnum)_random.Next(2)},
                            new Ship(2){Orient = (ShipOrientationEnum)_random.Next(2)},
                            new Ship(1){Orient = (ShipOrientationEnum)_random.Next(2)},
                            new Ship(1){Orient = (ShipOrientationEnum)_random.Next(2)},
                            new Ship(1){Orient = (ShipOrientationEnum)_random.Next(2)},
                            new Ship(1){Orient = (ShipOrientationEnum)_random.Next(2)}
                        };
                return ships;
            }
            if (modeButtonSettings == 0)
            {
                var ships = new List<Ship>                
                        {
                            new Ship(4){Orient = (ShipOrientationEnum)_random.Next(2)},
                            new Ship(4){Orient = (ShipOrientationEnum)_random.Next(2)},
                            new Ship(3){Orient = (ShipOrientationEnum)_random.Next(2)},
                            new Ship(3){Orient = (ShipOrientationEnum)_random.Next(2)},
                            new Ship(3){Orient = (ShipOrientationEnum)_random.Next(2)},
                            new Ship(2){Orient = (ShipOrientationEnum)_random.Next(2)},
                            new Ship(2){Orient = (ShipOrientationEnum)_random.Next(2)},                        
                            new Ship(1){Orient = (ShipOrientationEnum)_random.Next(2)},
                            new Ship(1){Orient = (ShipOrientationEnum)_random.Next(2)},
                            new Ship(1){Orient = (ShipOrientationEnum)_random.Next(2)},
                        };

                return ships;
            }
            var shipss = new List<Ship>();
            return shipss;
        }

// Реакція на постріли бійців
        public ShotingEnum OpenentShotAt(int x, int y)
        {
            var ship = GetShipAt(x, y);
            if (ship == null)
            {
                using (var soundPlayer = new SoundPlayer(Resource1.shotmiss))
                {
                    soundPlayer.Play();
                }                
                _cells[x, y].State = TableCellCollocateEnum.Missed;
                return ShotingEnum.Mis;
            }

            using (var soundPlayer = new SoundPlayer(Resource1.shotok))
            {
                soundPlayer.Play();
            }
            _cells[x, y].State = TableCellCollocateEnum.ShotShip;
            ship.HitCount++;
            if (ship.Drowned)
            {
                using (var soundPlayer = new SoundPlayer(Resource1.startbuton))
                {
                    soundPlayer.Play();
                }              
                DrawAround(ship, TableCellCollocateEnum.Missed);
                DrawShip(ship, TableCellCollocateEnum.Drowned, true);                            
            }
            return ship.Drowned ? ShotingEnum.Drowned : ShotingEnum.Hit;
        }    
    }
}
